package com.todo.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.todo.dto.LoginRequest;
import com.todo.entity.SmsCode;
import com.todo.entity.User;
import com.todo.mapper.SmsCodeMapper;
import com.todo.mapper.TodoMapper;
import com.todo.mapper.UserMapper;
import com.todo.service.UserService;
import com.todo.utils.JwtUtil;
import com.todo.utils.PhoneEncryptUtil;
import com.todo.utils.SmsUtil;
import com.todo.utils.SmsUtil.SmsResult;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 用户服务实现类
 * 实现用户相关的业务逻辑，包括登录、注册、短信验证等功能
 * 支持手机号AES加密存储，保护用户隐私数据安全
 */
@SuppressWarnings("all")
@Slf4j
@Service
public class UserServiceImpl implements UserService {

    // 注入用户数据访问层
    @Autowired
    private UserMapper userMapper;

    // 注入短信验证码数据访问层
    @Autowired
    private SmsCodeMapper smsCodeMapper;

    // 注入待办事项数据访问层
    @Autowired
    private TodoMapper todoMapper;

    // 注入JWT工具类
    @Autowired
    private JwtUtil jwtUtil;

    // 注入Redis模板
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    // 注入手机号加密工具类
    @Autowired
    private PhoneEncryptUtil phoneEncryptUtil;
    
    // 注入短信发送工具类
    @Autowired
    private SmsUtil smsUtil;

    /**
     * 发送短信验证码
     * @param phone 手机号码（明文）
     */
    @Override
    public void sendSmsCode(String phone) {
        // 生成6位随机数字验证码
        String code = RandomUtil.randomNumbers(6);
        
        // 创建短信验证码对象并保存到数据库
        // 注意：短信验证码表中的手机号保持明文存储，用于发送短信服务
        SmsCode smsCode = new SmsCode();
        smsCode.setPhone(phone); // 短信验证码表中保持明文
        smsCode.setCode(code);
        smsCode.setExpireTime(LocalDateTime.now().plusMinutes(5)); // 设置5分钟过期
        smsCode.setUsed(0); // 初始状态为未使用
        smsCodeMapper.insert(smsCode);
        
        // 同时保存验证码到Redis，用于快速验证（5分钟过期）
        stringRedisTemplate.opsForValue().set("sms_code:" + phone, code, 5, TimeUnit.MINUTES);
        
        // 使用短信工具类发送验证码短信
        try {
            SmsResult result = smsUtil.sendVerificationCode(phone, code);
            if (result.isSuccess()) {
                log.info("短信发送成功，手机号: {}, 验证码: {}", 
                    PhoneEncryptUtil.maskPhone(phone), code);
            } else {
                log.error("短信发送失败，手机号: {}, 错误信息: {}", 
                    PhoneEncryptUtil.maskPhone(phone), result.getMessage());
                // 即使短信发送失败，验证码仍然有效（存储在数据库和Redis中）
                // 这样可以在短信服务不可用时，仍然允许用户使用验证码
            }
        } catch (Exception e) {
            log.error("短信发送异常，手机号: {}, 异常信息: {}", 
                PhoneEncryptUtil.maskPhone(phone), e.getMessage(), e);
            // 短信发送异常不影响验证码的存储，确保系统可用性
        }
    }

    /**
     * 手机号验证码登录
     * @param request 登录请求对象，包含手机号和验证码
     * @return JWT令牌
     */
    @Override
    public String login(LoginRequest request) {
        String phone = request.getPhone();
        String code = request.getCode();
        
        // 从Redis中获取存储的验证码进行验证
        String storedCode = stringRedisTemplate.opsForValue().get("sms_code:" + phone);
        if (storedCode == null || !storedCode.equals(code)) {
            log.warn("验证码验证失败，手机号: {}", PhoneEncryptUtil.maskPhone(phone));
            throw new RuntimeException("验证码错误或已过期");
        }
        
        // 根据手机号查询用户，如果不存在则创建新用户
        User user = getUserByPhone(phone);
        if (user == null) {
            // 创建新用户，手机号加密存储
            user = new User();
            user.setPhone(phoneEncryptUtil.encrypt(phone)); // 加密存储手机号
            user.setNickname("用户" + phone.substring(7)); // 生成默认昵称
            user.setStatus(1); // 设置状态为正常
            userMapper.insert(user);
            
            log.info("创建新用户成功，手机号: {}, 用户ID: {}", 
                PhoneEncryptUtil.maskPhone(phone), user.getId());
        } else {
            log.info("用户登录成功，手机号: {}, 用户ID: {}", 
                PhoneEncryptUtil.maskPhone(phone), user.getId());
        }
        
        // 验证成功后清除Redis中的验证码，防止重复使用
        stringRedisTemplate.delete("sms_code:" + phone);
        
        // 生成并返回JWT令牌
        return jwtUtil.generateToken(user.getId());
    }

    /**
     * 根据手机号查询用户（支持加密手机号匹配）
     * @param phone 手机号码（明文）
     * @return 用户对象，如果不存在返回null
     */
    @Override
    public User getUserByPhone(String phone) {
        try {
            // 由于手机号是加密存储的，需要遍历所有用户进行解密匹配
            // 注意：这种方式在用户量很大时性能较差，可考虑使用手机号哈希值建立索引
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.select("id", "phone", "nickname", "avatar", "email", "bio", "wechat_openid", "status", "create_time", "update_time");
            List<User> allUsers = userMapper.selectList(queryWrapper);
            
            for (User user : allUsers) {
                try {
                    // 解密数据库中的手机号与输入的手机号进行比较
                    String decryptedPhone = phoneEncryptUtil.decrypt(user.getPhone());
                    if (phone.equals(decryptedPhone)) {
                        log.debug("找到匹配用户，用户ID: {}", user.getId());
                        return user;
                    }
                } catch (Exception e) {
                    // 可能存在未加密的历史数据，直接比较
                    if (phone.equals(user.getPhone())) {
                        log.warn("发现未加密的手机号数据，用户ID: {}, 建议执行数据迁移", user.getId());
                        return user;
                    }
                    log.debug("解密手机号失败，跳过用户ID: {}", user.getId());
                }
            }
            
            log.debug("未找到匹配的用户，手机号: {}", PhoneEncryptUtil.maskPhone(phone));
            return null;
            
        } catch (Exception e) {
            log.error("查询用户失败，手机号: {}", PhoneEncryptUtil.maskPhone(phone), e);
            throw new RuntimeException("查询用户失败", e);
        }
    }

    /**
     * 根据用户ID查询用户
     * @param id 用户ID
     * @return 用户对象（手机号已解密）
     */
    @Override
    public User getUserById(Long id) {
        User user = userMapper.selectById(id);
        if (user != null && user.getPhone() != null) {
            try {
                // 解密手机号用于返回给前端（脱敏显示）
                String decryptedPhone = phoneEncryptUtil.decrypt(user.getPhone());
                // 为了前端显示，可以选择返回脱敏手机号
                user.setPhone(PhoneEncryptUtil.maskPhone(decryptedPhone));
                log.debug("查询用户成功，用户ID: {}", id);
            } catch (Exception e) {
                log.warn("解密用户手机号失败，用户ID: {}, 可能是历史明文数据", id);
                // 如果是历史明文数据，进行脱敏处理
                user.setPhone(PhoneEncryptUtil.maskPhone(user.getPhone()));
            }
        }
        return user;
    }

    /**
     * 更新用户信息
     * @param user 用户对象
     */
    @Override
    public void updateUser(User user) {
        // 如果更新的用户对象包含手机号，需要加密处理
        if (user.getPhone() != null && !user.getPhone().trim().isEmpty()) {
            // 检查是否是脱敏显示的手机号（包含*号），如果是则不更新手机号
            if (!user.getPhone().contains("*")) {
                try {
                    user.setPhone(phoneEncryptUtil.encrypt(user.getPhone()));
                    log.info("更新用户信息，手机号已加密，用户ID: {}", user.getId());
                } catch (Exception e) {
                    log.error("加密用户手机号失败，用户ID: {}", user.getId(), e);
                    throw new RuntimeException("更新用户信息失败", e);
                }
            } else {
                // 如果是脱敏手机号，不更新phone字段
                user.setPhone(null);
                log.debug("跳过脱敏手机号更新，用户ID: {}", user.getId());
            }
        }
        
        userMapper.updateById(user);
        log.info("用户信息更新成功，用户ID: {}", user.getId());
    }
    
    /**
     * 获取用户统计数据
     * 包括总任务数、已完成任务数、待完成任务数等
     * @param userId 用户ID
     * @return 统计数据Map
     */
    @Override
    public java.util.Map<String, Object> getUserStats(Long userId) {
        java.util.Map<String, Object> stats = new java.util.HashMap<>();
        
        try {
            // 构建查询条件：查询指定用户且未删除的待办事项
            QueryWrapper<com.todo.entity.Todo> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id", userId);
            queryWrapper.ne("status", 2); // 排除已删除的待办事项
            
            // 查询总任务数
            Long totalTodos = todoMapper.selectCount(queryWrapper);
            
            // 构建查询条件：查询已完成的任务
            QueryWrapper<com.todo.entity.Todo> completedWrapper = new QueryWrapper<>();
            completedWrapper.eq("user_id", userId);
            completedWrapper.eq("status", 1); // 状态为已完成
            Long completedTodos = todoMapper.selectCount(completedWrapper);
            
            // 构建查询条件：查询待完成的任务
            QueryWrapper<com.todo.entity.Todo> pendingWrapper = new QueryWrapper<>();
            pendingWrapper.eq("user_id", userId);
            pendingWrapper.eq("status", 0); // 状态为待完成
            Long pendingTodos = todoMapper.selectCount(pendingWrapper);
            
            // 组装统计数据，确保非空值
            stats.put("totalTodos", totalTodos != null ? totalTodos.intValue() : 0);
            stats.put("completedTodos", completedTodos != null ? completedTodos.intValue() : 0);
            stats.put("pendingTodos", pendingTodos != null ? pendingTodos.intValue() : 0);
            
            log.debug("获取用户统计数据成功，用户ID: {}", userId);
            return stats;
        } catch (Exception e) {
            // 异常处理：记录错误日志并返回默认统计数据
            log.error("获取用户统计数据失败，用户ID: {}", userId, e);
            stats.put("totalTodos", 0);
            stats.put("completedTodos", 0);
            stats.put("pendingTodos", 0);
            return stats;
        }
    }
    
    /**
     * 数据迁移方法：将现有明文手机号加密
     * 注意：此方法仅用于数据迁移，生产环境使用时需要谨慎
     */
    public void migratePhoneEncryption() {
        log.info("开始执行手机号加密迁移...");
        
        try {
            // 查询所有用户
            List<User> allUsers = userMapper.selectList(null);
            int successCount = 0;
            int skipCount = 0;
            
            for (User user : allUsers) {
                try {
                    String phone = user.getPhone();
                    
                    // 检查是否已经是加密的手机号（通过长度和格式判断）
                    if (phone != null && phone.length() == 11 && phone.matches("^1[3-9]\\d{9}$")) {
                        // 明文手机号，需要加密
                        String encryptedPhone = phoneEncryptUtil.encrypt(phone);
                        user.setPhone(encryptedPhone);
                        userMapper.updateById(user);
                        successCount++;
                        log.info("用户手机号加密成功，用户ID: {}, 原手机号: {}", 
                            user.getId(), PhoneEncryptUtil.maskPhone(phone));
                    } else {
                        // 已经加密或格式异常，跳过
                        skipCount++;
                        log.debug("跳过用户手机号，用户ID: {} (已加密或格式异常)", user.getId());
                    }
                } catch (Exception e) {
                    log.error("加密用户手机号失败，用户ID: {}", user.getId(), e);
                }
            }
            
            log.info("手机号加密迁移完成，成功: {}, 跳过: {}", successCount, skipCount);
            
        } catch (Exception e) {
            log.error("手机号加密迁移失败", e);
            throw new RuntimeException("数据迁移失败", e);
        }
    }
} 